package com.atlin.project.service.impl;

import com.atlin.project.common.ErrorCode;
import com.atlin.project.contant.CommonConstant;
import com.atlin.project.exception.BusinessException;
import com.atlin.project.manager.AsyncManager;
import com.atlin.project.mapper.TaskListMapper;
import com.atlin.project.model.domain.SysUser;
import com.atlin.project.model.domain.TaskList;
import com.atlin.project.model.dto.taskList.TaskListAddRequest;
import com.atlin.project.model.dto.taskList.TaskListQueryRequest;
import com.atlin.project.model.enums.taskListEnums.TaskListFrequencyEnum;
import com.atlin.project.model.enums.taskListEnums.TaskListStatusEnum;
import com.atlin.project.model.enums.taskListEnums.TaskTypeEnum;
import com.atlin.project.model.vo.taskList.TaskListVO;
import com.atlin.project.service.SysUserService;
import com.atlin.project.service.TaskListService;

import com.atlin.project.utils.RedisUtils;
import com.atlin.project.utils.sql.SqlUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author atlin
 * @description 针对表【task_list(任务清单)】的数据库操作Service实现
 * @createDate 2024-11-04 19:25:03
 */
@Service
@Slf4j
public class TaskListServiceImpl extends ServiceImpl<TaskListMapper, TaskList>
        implements TaskListService {

    private static final String TASKLIST_CACHE_KEY = "taskList:";

    @Resource
    private SysUserService sysUserService;

    @Resource
    private RedisUtils redisUtils;

    private final static Gson GSON = new Gson();

    @Override
    public void valid(TaskList taskList, boolean add) {
        String frequency = taskList.getFrequency();
        String taskType = taskList.getTaskType();

        if (StringUtils.isNotBlank(frequency)) {
            TaskListFrequencyEnum taskListFrequencyEnum = TaskListFrequencyEnum.getEnumByValue(frequency);
            if (taskListFrequencyEnum == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的频率");
            }
        }
        if (StringUtils.isBlank(taskType)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请选择任务类型");
        }

        TaskTypeEnum taskTypeEnum = TaskTypeEnum.getEnumByValue(taskType);
        if (taskTypeEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的任务类型");
        }

    }

    @Override
    @Transactional
    public boolean saveTaskList(TaskListAddRequest taskListAddRequest, SysUser loginUser) {
        List<String> tags = taskListAddRequest.getTags();
        LocalDateTime endDate = taskListAddRequest.getEndDate();
        String frequency = taskListAddRequest.getFrequency();
        String taskType = taskListAddRequest.getTaskType();

        boolean res = false;
        // 判断任务类型 单次或循环
        if (StringUtils.isNotBlank(taskType)) {
            TaskTypeEnum taskTypeEnum = TaskTypeEnum.getEnumByValue(taskType);
            if (taskTypeEnum == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的任务类型");
            }
            // 单次
            if (TaskTypeEnum.ONCE.getValue().equals(taskTypeEnum.getValue())) {
                TaskList taskList = new TaskList();
                BeanUtils.copyProperties(taskListAddRequest, taskList);
                if (!CollectionUtils.isEmpty(tags)) {
                    taskList.setTags(GSON.toJson(tags));
                }
                taskList.setUserId(loginUser.getId());
                buildParams(taskList);
                res = save(taskList);
            } else if (TaskTypeEnum.CYCLE.getValue().equals(taskTypeEnum.getValue())) {
                // 判断频率 日 周 月
                TaskListFrequencyEnum frequencyEnum = TaskListFrequencyEnum.getEnumByValue(frequency);
                if (frequencyEnum == null) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "请选择任务频率");
                }

                switch (frequencyEnum) {
                    case DAILY: {
                        LocalDateTime now = LocalDateTime.now();
                        ArrayList<TaskList> taskLists = new ArrayList<>();
                        while (now.isBefore(endDate)) {
                            buildTaskLists(taskListAddRequest, loginUser, tags, now, taskLists);

                            now = now.plusDays(1);
                        }
                        res = saveBatch(taskLists);
                        break;
                    }
                    case WEEKLY: {
                        LocalDateTime now = LocalDateTime.now();
                        ArrayList<TaskList> taskLists = new ArrayList<>();
                        while (now.isBefore(endDate)) {
                            buildTaskLists(taskListAddRequest, loginUser, tags, now, taskLists);

                            now = now.plusWeeks(1);
                        }
                        res = saveBatch(taskLists);
                        break;
                    }
                    case MONTHLY: {
                        LocalDateTime now = LocalDateTime.now();
                        ArrayList<TaskList> taskLists = new ArrayList<>();
                        while (now.isBefore(endDate)) {
                            buildTaskLists(taskListAddRequest, loginUser, tags, now, taskLists);

                            now = now.plusMonths(1);
                        }
                        res = saveBatch(taskLists);
                        break;
                    }
                }
            }
        }
        return res;
    }

    /**
     * 构造需要存储的任务
     *
     * @param taskListAddRequest
     * @param loginUser
     * @param tags
     * @param now
     * @param taskLists
     */
    private void buildTaskLists(TaskListAddRequest taskListAddRequest, SysUser loginUser, List<String> tags, LocalDateTime now, ArrayList<TaskList> taskLists) {
        TaskList taskList = new TaskList();
        BeanUtils.copyProperties(taskListAddRequest, taskList);
        if (!CollectionUtils.isEmpty(tags)) {
            taskList.setTags(GSON.toJson(tags));
        }
        taskList.setUserId(loginUser.getId());
        taskList.setStartDate(now.withHour(0).withMinute(0).withSecond(0).withNano(0));
        taskList.setEndDate(now.withHour(23).withMinute(59).withSecond(59).withNano(0));

        // 处理状态
        buildParams(taskList);
        taskLists.add(taskList);
    }


    /**
     * 构建时间 状态等任务参数
     *
     * @param taskList
     */
    private void buildParams(TaskList taskList) {
        LocalDateTime startDate = taskList.getStartDate();
        LocalDateTime endDate = taskList.getEndDate();

        /**
         * 没有开始时间或结束时间 默认为当日任务
         */
        if (startDate == null) {
            taskList.setStartDate(LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0));
            startDate = taskList.getStartDate();
        }

        if (endDate == null) {
            taskList.setEndDate(LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).withNano(0));
            endDate = taskList.getEndDate();
        }

        if (startDate.isAfter(endDate)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "开始时间不能大于结束时间");
        }

        LocalDateTime now = LocalDateTime.now();

        boolean isBetween = now.isAfter(startDate) && now.isBefore(endDate);
        if (isBetween) {
            taskList.setStatus(TaskListStatusEnum.RUNNING.getValue());
        } else if (now.isBefore(startDate)) {
            taskList.setStatus(TaskListStatusEnum.NO_START.getValue());
        } else {
            taskList.setStatus(TaskListStatusEnum.EXPIRED.getValue());
        }
    }

    @Override
    public TaskListVO getTaskListVO(TaskList taskList, HttpServletRequest request) {
        TaskListVO taskListVO = TaskListVO.objToVo(taskList);
        // todo other operation
        return taskListVO;
    }

    /**
     * 获取查询包装类（用户根据哪些字段查询，根据前端传来的请求对象，得到 mybatis 框架支持的查询 QueryWrapper 类）
     *
     * @param taskListQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<TaskList> getQueryWrapper(TaskListQueryRequest taskListQueryRequest) {
        QueryWrapper<TaskList> queryWrapper = new QueryWrapper<>();
        if (taskListQueryRequest == null) {
            return queryWrapper;
        }
        Long id = taskListQueryRequest.getId();
        String title = taskListQueryRequest.getTitle();
        String searchText = taskListQueryRequest.getSearchText();
        String description = taskListQueryRequest.getDescription();
        List<String> tags = taskListQueryRequest.getTags();
        LocalDateTime startDate = taskListQueryRequest.getStartDate();
        LocalDateTime endDate = taskListQueryRequest.getEndDate();
        String frequency = taskListQueryRequest.getFrequency();
        Long userId = taskListQueryRequest.getUserId();
        Integer status = taskListQueryRequest.getStatus();
        String sortField = taskListQueryRequest.getSortField();
        String sortOrder = taskListQueryRequest.getSortOrder();

        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        queryWrapper.like(StringUtils.isNotBlank(description), "description", description);
        queryWrapper.eq(ObjectUtils.isNotEmpty(frequency), "frequency", frequency);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(status), "status", status);
        queryWrapper.ge(startDate != null, "startDate", startDate);
        queryWrapper.le(endDate != null, "endDate", endDate);
        queryWrapper.eq("isDelete", false);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);

        if (!CollectionUtils.isEmpty(tags)) {
            queryWrapper.lambda().in(TaskList::getTags, tags);

        }
        if (StringUtils.isNotBlank(searchText)) {
            queryWrapper.lambda()
                    .or().like(TaskList::getTitle, searchText)
                    .or().like(TaskList::getDescription, searchText)
                    .or().like(TaskList::getTags, searchText);
        }
        return queryWrapper;
    }

    @Override
    public Page<TaskListVO> getTaskListVOPage(Page<TaskList> taskListPage, HttpServletRequest request) {
        List<TaskList> taskListList = taskListPage.getRecords();
        Page<TaskListVO> taskListVOPage = new Page<>(taskListPage.getCurrent(), taskListPage.getSize(), taskListPage.getTotal());
        if (CollectionUtils.isEmpty(taskListList)) {
            return taskListVOPage;
        }

        // 填充信息
        List<TaskListVO> taskListVOList = taskListList.stream().map(taskList -> {
            TaskListVO taskListVO = TaskListVO.objToVo(taskList);

            return taskListVO;
        }).collect(Collectors.toList());
        taskListVOPage.setRecords(taskListVOList);
        return taskListVOPage;
    }


    @Override
    public String getTodayTaskList(String mpOpenId) {
        // 从Redis查询任务
        String key = TASKLIST_CACHE_KEY + mpOpenId;
        Object res = redisUtils.get(key);
        return res == null ? "人生得意须尽欢，莫使金樽空对月。\n 今日暂无任务" : res.toString();
    }

    @Override
    public void buildTaskListCache() {
        // todo 细粒度更新
        // todo 查询优化
        //select * from task_list where status = 0  --->未完成的所有任务
        QueryWrapper<TaskList> wrapper = new QueryWrapper<>();
        wrapper.eq("status", TaskListStatusEnum.RUNNING.getValue());
        List<TaskList> taskLists = this.list(wrapper);

        if (taskLists.isEmpty()) {
            return;
        }
        //startDate >= currentDate <= endDate 今日任务
        //WEEKLY 本周目标
        //MONTHLY 本月目标

        Map<Long, List<TaskList>> taskListMap = taskLists.stream().collect(Collectors.groupingBy(TaskList::getUserId));

        QueryWrapper<SysUser> sysUserQueryWrapper = new QueryWrapper<>();
        sysUserQueryWrapper.in("id", taskListMap.keySet());
        sysUserQueryWrapper.isNotNull("mpOpenId");

        List<SysUser> userList = sysUserService.list(sysUserQueryWrapper);

        for (SysUser sysUser : userList) {
            String val = buildTaskLists(taskListMap.get(sysUser.getId()));
            String key = TASKLIST_CACHE_KEY + sysUser.getMpOpenId();

            redisUtils.set(key, val, 1, TimeUnit.DAYS);
        }
    }

    private static String buildTaskLists(List<TaskList> taskLists) {
        taskLists = taskLists.stream().sorted(Comparator.comparing(TaskList::getFrequency)).collect(Collectors.toList());
        StringBuilder builder = new StringBuilder();
        TaskListFrequencyEnum taskListFrequencyEnum;
        Integer needTime = 0;
        for (int i = 0; i < taskLists.size(); i++) {
            TaskList taskList = taskLists.get(i);
            needTime += taskList.getNeedTime();
            taskListFrequencyEnum = TaskListFrequencyEnum.getEnumByValue(taskList.getFrequency());
            builder.append("\n").append(i + 1).append(": ");
            builder.append(taskList.getTitle()).append("(")
                    .append(taskList.getNeedTime()).append("分钟) [")
                    .append(taskListFrequencyEnum.getText()).append("]").append("\n  ");
            builder.append(taskList.getDescription()).append("\n  ");
            builder.append(taskList.getTags()).append("\n");
        }
        builder.insert(0, "今日任务: (总时长: " + needTime + "分钟)");
        return builder.toString();
    }

    @Override
    public void updateTaskListStatus() {
        LocalDateTime startDate = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endDate = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).withNano(0);

        // 未开始的任务是否到了开始时间
        UpdateWrapper<TaskList> updateWrapper = new UpdateWrapper<>();
        // where status = 0 and startDate <= current_timestamp;
        updateWrapper.lambda().eq(TaskList::getStatus, TaskListStatusEnum.NO_START.getValue())
                .ge(TaskList::getStartDate, startDate)
                .le(TaskList::getEndDate, endDate);
        
        updateWrapper.lambda().set(TaskList::getStatus, TaskListStatusEnum.RUNNING.getValue());
        this.update(updateWrapper);
        // 进行中的任务是否已过期
        updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(TaskList::getStatus, TaskListStatusEnum.RUNNING.getValue())
                .lt(TaskList::getEndDate, startDate);
        
        updateWrapper.lambda().set(TaskList::getStatus, TaskListStatusEnum.EXPIRED.getValue());

        this.update(updateWrapper);
    }


    @Override
    public void doUpdateCache(int condition) {
        if (TaskListStatusEnum.RUNNING.getValue() == condition) {
            AsyncManager.me().execute(new TimerTask() {
                @Override
                public void run() {
                    log.info(Thread.currentThread().getName() + " buildTaskListCache execute start");
                    buildTaskListCache();
                    log.info(Thread.currentThread().getName() + " buildTaskListCache execute end");
                }
            });
        }
    }
}




